package com.example.gobang.game;

import com.example.gobang.GobangApplication;
import com.example.gobang.mapper.UserMapper;
import com.example.gobang.model.UserInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.util.UUID;

/**
 * Description:
 * Date: 2024-09-05
 * Time: 22:17
 */
@Data
@Slf4j
public class Room {
    private String roomId; // 使用字符串来表示，方便生成唯一值
    private UserInfo user1;
    private UserInfo user2;
    private Integer blackUser; // 执黑棋的玩家先手

    private ObjectMapper objectMapper = new ObjectMapper();

    // @Autowired
    private OnlineUserManager onlineUserManager;

    private RoomManager roomManager;

    private UserMapper userMapper;

    private static final Integer MAX_ROW = 15;
    private static final Integer MAX_COL = 15;

    // 约定当前位置
    // 0：未落子  1：user1 落子位置  2：user2 落子位置
    private int[][] board = new int[MAX_ROW][MAX_COL];

    public Room() {
        // 使用 UUID 来生成唯一的房间 ID
        roomId = UUID.randomUUID().toString();
        // 通过入口类中记录的 context 来手动获取前面的 RoomManager 和 OnlineUserManager
        roomManager = GobangApplication.context.getBean(RoomManager.class);
        onlineUserManager = GobangApplication.context.getBean(OnlineUserManager.class);
        userMapper = GobangApplication.context.getBean(UserMapper.class);
     }

    /**
     * 这个方法来处理一次落子操作
     * @param reqJson
     */
    @SneakyThrows
    public void putChess(String reqJson) {
        // 1.记录当前落子的位置
        GameRequest request = objectMapper.readValue(reqJson,GameRequest.class);
        GameResponse response = new GameResponse();
        // 根据玩家 1 和玩家 2 来决定数组里是填写 1 还是 2
        int chess = request.getUserId() == user1.getUserId() ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if (board[row][col] != 0) {
            // 之前在客户端已经针对重复落子进行判定了. 这里为了使程序更加稳健, 在服务器这边再判定一次
            log.info("当前位置(" + row + "," + col + ") 已经有子了，请重新选择落子位置");
            return;
        }
        board[row][col] = chess;

        // 判定胜负
        int winner = checkWinner(row,col,chess);

        // 4.给房间中所有客户端都返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());
        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);

        // 要想给用户发送 WebSocket 数据，需要先获取这个用户的 WebSocketSession
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());
        // 如果查到当前会话为空，说明玩家已经下线，需要进行特殊处理
        if (session1 == null) {
            // 玩家一已经掉线，那直接判定玩家二获胜
            response.setWinner(user2.getUserId());
            log.info("玩家一掉线");
        }
        if (session2 == null) {
            // 玩家二已经掉线，那直接判定玩家一获胜
            response.setWinner(user1.getUserId());
            log.info("玩家二掉线");
        }
        // 将响应构造成 JSON 字符串，通过 Session 来传输
        String respJson = objectMapper.writeValueAsString(response);
        if (session1 != null) {
            session1.sendMessage(new TextMessage(respJson));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(respJson));
        }

        // 如果当前已经分出胜负，那就可以销毁房间了（把房间从房间管理器中移除）
        if (response.getWinner() != 0) {
            log.info("游戏结束，即将退出房间。roomId = " + roomId + " 获胜方为：" + response.getWinner());
            // 更新获胜方和失败方的信息
            int winUserId = response.getWinner();
            int loseUserId = (response.getWinner().equals(user1.getUserId())) ? user2.getUserId() : user1.getUserId();
            userMapper.userWin(winUserId);
            userMapper.userLose(loseUserId);

            // 销毁房间
            roomManager.remove(roomId, user1.getUserId(), user2.getUserId());
        }
    }

    private void printBoard() {
        System.out.println("[打印棋盘信息] " + roomId);
        System.out.println("===========================================");
        for (int i = 0;i < MAX_ROW;i++) {
            for (int j = 0;j < MAX_COL;j++) {
                System.out.print(board[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("===========================================");
    }

    private int checkWinner(int row, int col, int chess) {
        // 1.检查所有的行
        // 先枚举最左边棋子相对当前所下棋子的位置，从这个位置开始往右找是否有五子连线
        for (int c = col - 4; c <= col; c++) {
            try {
                if (board[row][c] == chess
                        && board[row][c + 1] == chess
                        && board[row][c + 2] == chess
                        && board[row][c + 3] == chess
                        && board[row][c + 4] == chess) {
                    // 横行构成五子连珠
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            } catch (IndexOutOfBoundsException e) {
                // 如果越界了，那就直接忽略掉
                continue;
            }
        }

        // 检查所有的列
        for (int r = row - 4; r <= row; r++) {
            try {
                if (board[r][col] == chess
                        && board[r + 1][col] == chess
                        && board[r + 2][col] == chess
                        && board[r + 3][col] == chess
                        && board[r + 4][col] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            } catch (IndexOutOfBoundsException e) {
                continue;
            }
        }

        // 检查副对角线
        for (int i = row - 4,j = col - 4; i <= row && j <= col; i++,j++) {
            try {
                if (board[i][j] == chess
                        && board[i + 1][j + 1] == chess
                        && board[i + 2][j + 2] == chess
                        && board[i + 3][j + 3] == chess
                        && board[i + 4][j + 4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            } catch (IndexOutOfBoundsException e) {
                continue;
            }
        }

        // 检查主对角线
        for (int i = row - 4, j = col + 4; i <= row && j >= col; i++, j--) {
            try {
                if (board[i][j] == chess
                        && board[i + 1][j - 1] == chess
                        && board[i + 2][j - 2] == chess
                        && board[i + 3][j - 3] == chess
                        && board[i + 4][j - 4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            } catch (IndexOutOfBoundsException e) {
                continue;
            }
        }


        return 0;
    }
}
